﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PriceLibrary.PriceService;
using PriceLibrary.ChartService;
using PriceLibrary.Enums;
using PriceLibrary.Utilities;
using log4net;
using System.ComponentModel;

namespace VDataAdapter.Communication
{
    public class MessageBuilder : IVdataMessageBuilder
    {
        #region Variable
        string[] PropertyNameRejected = {"StockNo", "StockSymbol", "TradingDate" };
        string PropertyTypeRejected = "System.Runtime.Serialization.ExtensionDataObject";
        List<string> ListPropertyNameRejected;
        private readonly ILog _log = LogManager.GetLogger(typeof(MessageBuilder));
        DataSource _datasource = new DataSource();
        MessageBuilderRecorder _recorder = new MessageBuilderRecorder();

        #endregion

        #region Contructors
        public MessageBuilder()
        {    
            ListPropertyNameRejected = new List<string>(PropertyNameRejected.Length);
            ListPropertyNameRejected.AddRange(PropertyNameRejected);
        }
        #endregion

        #region Public Methods
        
        /// <summary>
        /// Build the message object struct included values and properties of new object and old object
        /// </summary>
        /// <param name="e"></param>
        /// <param name="objNew">New Object</param>
        /// <param name="objOld">Old Object</param>
        /// <returns>Return a message string have struct along object parameter</returns>
        /*
        public string BuildMessageObject(MessageType e, Object objNew, Object objOld)
        {
            string strMsgObj = e.ToString().ToUpper() + "#";
            string strNew = String.Empty;
            string strOld = String.Empty;
            try
            {
                PropertyDescriptorCollection properties = GetPropertiesByOjbect(e);
                foreach (PropertyDescriptor prop in properties)
                {
                    strNew += prop.GetValue(objNew).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                    strOld += prop.GetValue(objOld).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strNew + "#" + strOld;
            }
            catch (Exception ex)
            {
                strMsgObj = ex.Message;
            }
            return strMsgObj;
        }
        */

        /// <summary>
        /// /// Build the message object struct included values and properties of StockInfoStatic object, new Stock object and old Stock object (only stock)
        /// </summary>
        /// <param name="objStatic"></param>
        /// <param name="e"></param>
        /// <param name="objNew"></param>
        /// <param name="objOld"></param>
        /// <returns>Return a message string have struct along object parameter</returns>
        /*
        public string BuildMessageStockObject(MessageType e, PriceLibrary.PriceService.StockInfoStatic objStatic, 
            Object objNew, Object objOld)
        {
            string strMsgObj = e.ToString().ToUpper() + "#";
            string strNew = String.Empty;
            string strOld = String.Empty;
            string strStaticMsg = BuildMsgStockInfoStatic(objStatic);
            try
            {
                PropertyDescriptorCollection properties = GetPropertiesByOjbect(e);
                foreach (PropertyDescriptor prop in properties)
                {
                    if (!prop.Name.Trim().ToLower().Equals("StockNo".ToLower()) && prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        strNew += prop.GetValue(objNew).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                        strOld += prop.GetValue(objOld).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                    }
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strStaticMsg + strNew + "#" + strStaticMsg + strOld;                
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageStockObject", ex);
            }
            return strMsgObj;
        }
        */


        private string GetPropertyMessageMarket(PriceLibrary.PriceService.MarketInfo obj, PropertyDescriptor prop)
        {
            string strMsg = String.Empty;
            if (prop.PropertyType.ToString() == PropertyTypeRejected)
                throw new Exception("PropertyTypeRejected exception GetPropertyMessageMarket");
            else
            {
                if (prop.Name == "TradingDate")
                {
                    strMsg += "12:00:00" + "|" ?? DBNull.Value.ToString() + "|";

                }
                else if (prop.Name == "TradingTime")
                {
                    var time = int.Parse(prop.GetValue(obj).ToString());
                    strMsg += time.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
                }
                else if (prop.Name == "FloorCode")
                {
                    var time = int.Parse(prop.GetValue(obj).ToString());
                    strMsg += time.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
                }
                else
                {
                    strMsg += GetPropertyByDefault(prop, obj);
                    //strMsg += prop.GetValue(objNew).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                    //strOld += prop.GetValue(objOld).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                }
            }
            return strMsg;
                
        }

        private string BuildMessageMarketRefactor(PriceLibrary.PriceService.MarketInfo objNew,
            PriceLibrary.PriceService.MarketInfo objOld)
        {
            string strMsgObj = MessageType.MarketInfo.ToString().ToUpper() + "#";
            string strNew = String.Empty;
            string strOld = String.Empty;
            
            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.PriceService.MarketInfo));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        strNew += GetPropertyMessageMarket(objNew, prop);
                        strOld += GetPropertyMessageMarket(objOld, prop);
                    }
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strNew + "#" + strOld;
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageMarket", ex);
            }
            return strMsgObj;
        }


        /// <summary>
        /// Build the message MarketInfo struct included values and properties of new MarketInfo and old MarketInfo
        /// </summary>
        /// <param name="objNew">New market object </param>
        /// <param name="objOld">Old market object </param>
        /// <returns>Return a message string have struct along Market parameter</returns>
        public virtual string BuildMessageMarket(PriceLibrary.PriceService.MarketInfo objNew, 
            PriceLibrary.PriceService.MarketInfo objOld)
        {
            string strMsgObj = MessageType.MarketInfo.ToString().ToUpper() + "#";
            string strNew = String.Empty;
            string strOld = String.Empty;            
            
            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.PriceService.MarketInfo));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        if (prop.Name == "TradingDate")
                        {
                            strNew += "12:00:00" + "|" ?? DBNull.Value.ToString() + "|";
                            strOld += "12:00:00" + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        else if (prop.Name == "TradingTime")
                        {
                            var timeNew = int.Parse(prop.GetValue(objNew).ToString());
                            var timeOld = int.Parse(prop.GetValue(objOld).ToString());
                            strNew += timeNew.ToString("00:00:00")+ "|" ?? DBNull.Value.ToString() + "|";
                            strOld += timeOld.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        else if (prop.Name == "FloorCode")
                        {
                            var timeNew = int.Parse(prop.GetValue(objNew).ToString());
                            var timeOld = int.Parse(prop.GetValue(objOld).ToString());
                            strNew += timeNew.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
                            strOld += timeOld.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        else
                        {
                            strNew += GetPropertyByDefault(prop, objNew);
                            strOld += GetPropertyByDefault(prop, objOld);
                            //strNew += prop.GetValue(objNew).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                            //strOld += prop.GetValue(objOld).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                        }
                    }
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strNew + "#" + strOld;
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageMarket", ex);
            }
            return strMsgObj;
        }
        

        /// <summary>
        /// Build the message Stock struct included values and properties of new Stock and old Stock
        /// </summary>
        /// <param name="objNew"></param>
        /// <param name="objOld"></param>
        /// <returns>Return a message string have struct along Stock parameter</returns>
        public virtual string BuildMessageStock(PriceLibrary.PriceService.StockInfoStatic objStatic, 
            PriceLibrary.PriceService.StockInfoDyns objNew,PriceLibrary.PriceService.StockInfoDyns objOld)
        {
            string strMsgObj = MessageType.Stock.ToString().ToUpper() + "#";
            string strDynsMsgNew = String.Empty;
            string strDynsMsgOld = String.Empty;
            string strStaticMsg = BuildMsgStockInfoStatic(objStatic);
            try
            {  
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.PriceService.StockInfoDyns));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (!prop.Name.Trim().ToLower().Equals("StockNo".ToLower()) && !prop.Name.Trim().ToLower().Equals("TradingDate".ToLower())  && prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        if (prop.Name == "Time")
                        {
                            strDynsMsgNew += GetTimeValue(prop, objNew);
                            strDynsMsgOld += GetTimeValue(prop, objOld);
                            //var timeNew = int.Parse(prop.GetValue(objNew).ToString());
                            //var timeOld = int.Parse(prop.GetValue(objOld).ToString());
                            //strDynsMsgNew += timeNew.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
                            //strDynsMsgOld += timeOld.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
                        }                        
                        else
                        {
                            strDynsMsgNew += prop.GetValue(objNew).ToString().Trim() + "|" ?? prop.Name + "|";
                            strDynsMsgOld += prop.GetValue(objOld).ToString().Trim() + "|" ?? prop.Name + "|";
                        }
                        //strDynsMsgNew += prop.Name.Trim() + "|\n" ?? DBNull.Value.ToString() + "|\n";
                        //strDynsMsgOld += prop.Name.Trim() + "|\n" ?? DBNull.Value.ToString() + "|\n";
                    }
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strStaticMsg + strDynsMsgNew + "#" + strStaticMsg + strDynsMsgOld;
                //_log.WarnFormat("{0}", strMsgObj); 
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageStock", ex);
            }

            // _recorder.WriteBuildMessageStock(objStatic, objNew, objOld, strMsgObj);
            
            return strMsgObj;
        }

        private string GetTimeValue(PropertyDescriptor propertyDescriptor, object timeObj)
        {
            int time = int.Parse(propertyDescriptor.GetValue(timeObj).ToString());
            if (time == 0)
                return DateTime.Now.ToString("hh:mm:ss")+"|";
            else
                return time.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
        }
        
        /// <summary>
        /// Return Property Name if value is null or empty
        /// </summary>
        /// <param name="propertyDescriptor"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        private string GetPropertyStr(PropertyDescriptor propertyDescriptor, object property)
        {
            object value = propertyDescriptor.GetValue(property);
            if (value==null || value.ToString().Equals(""))
            {
                value = propertyDescriptor.Name;
            }

            return value.ToString().Trim().Replace("|", "").Replace("#","") + "|";
        }

        /// <summary>
        /// Return 0 if property is null or empty
        /// </summary>
        /// <param name="propertyDescriptor"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        private string GetPropertyByDefault(PropertyDescriptor propertyDescriptor, object property)
        {
            object value = propertyDescriptor.GetValue(property);
            if (value == null || value.ToString().Equals(""))
            {
                value = "0";
            }

            return value.ToString().Trim().Replace("|", "").Replace("#", "") + "|";
        }


        /// <summary>
        /// Mapped symbol to FloorCode:
        /// HOSE, VN30 >> "10"
        /// UPCOM >> "03"
        /// HNX, HNX30 >> "02"
        /// throw Exception otherwise
        /// </summary>
        /// <param name="symbol"></param>
        /// <returns></returns>
        private string GetFloorCode(string symbol)
        {
            if (symbol.Equals("HOSE", StringComparison.Ordinal) || symbol.Equals("VN30", StringComparison.Ordinal))
                return "10";
            if (symbol.Equals("UPCOM", StringComparison.Ordinal))
                return "03";
            if (symbol.Equals("HNX", StringComparison.Ordinal) || symbol.Equals("HNX30", StringComparison.Ordinal))
                return "02";
            else
                throw new Exception("Invalid Input");            
        }

        /// <summary>
        /// If symbol is one of HOSE, VN30, UPCOM, HNX, HNX30
        /// </summary>
        /// <param name="symbol"></param>
        /// <returns></returns>
        private bool IsMarketInfoSymbol(string symbol)
        {
            List<string> symbolList = new List<string>(new string[]{"HOSE", "VN30", "UPCOM", "HNX", "HNX30"});
            if (symbolList.Contains(symbol))
                return true;
            return false;
        }

        /// <summary>
        /// Get floor code if is a market info transaction symbol (HOSE, HNX etc)
        /// otherwise get the floor of that symbol from datasource
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="dataSource"></param>
        /// <returns></returns>
        private string GetFloorCode(PriceLibrary.ChartService.Transaction obj, DataSource dataSource)
        {
            if (IsMarketInfoSymbol(obj.Symbol))
                return GetFloorCode(obj.Symbol);
            else
            {
                if (dataSource.BasePrices.ContainsKey(obj.Symbol))
                {
                    var stockstatic = (PriceLibrary.PriceService.StockInfoStatic)_datasource.BasePrices[obj.Symbol];
                    return stockstatic.FloorCode.ToString();                    
                }
                else                
                    throw new KeyNotFoundException("Not found symbol in DataSource: " + obj.Symbol);
            }            
        }


        private string BuildMessageTransactionRefactor(PriceLibrary.ChartService.Transaction objNew, PriceLibrary.ChartService.Transaction objOld)
        {
            string strMsgObj = MessageType.Transaction.ToString().ToUpper() + "#";
            string strNew = String.Empty;
            string strOld = String.Empty;
            string FloorCode = "10";

            if (objNew.MatchType == null)
            {
                objNew.MatchType = "U";
                objOld.MatchType = "U";
            }

            try
            {
                FloorCode = GetFloorCode(objNew, _datasource);
            }
            catch (KeyNotFoundException ex)
            {
                _log.Error("BuildMessageTransaction error. Not found Symbol  " + objNew.Symbol + " in Datasource BasePrices", ex);
            }

            string actual = FloorCode;

            if (IsMarketInfoSymbol(objNew.Symbol))
                FloorCode = GetFloorCode(objNew.Symbol);
            else ///Get FloorCode From datasource.BasePrices base on Transaction.Symbol
            {
                try
                {
                    var stockstatic = (PriceLibrary.PriceService.StockInfoStatic)_datasource.BasePrices[objNew.Symbol];
                    FloorCode = stockstatic.FloorCode.ToString();
                }
                catch (Exception ex)
                {
                    _log.Error("BuildMessageTransaction error. Not found Symbol  " + objNew.Symbol + " in Datasource BasePrices", ex);
                }
            }

            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.ChartService.Transaction));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        strNew += GetPropertyTransactionStr(objNew, prop);
                        strOld += GetPropertyTransactionStr(objOld, prop);
                    }
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strNew + FloorCode + "#" + strOld + FloorCode;
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageTransaction", ex);
            }

            return strMsgObj;
        }

        private string GetPropertyTransactionStr(PriceLibrary.ChartService.Transaction transaction, PropertyDescriptor propertyDescriptor)
        {
            if (propertyDescriptor.PropertyType.ToString() != PropertyTypeRejected)
            {
                if (propertyDescriptor.Name == "Time")
                {
                    return GetTimeValue(propertyDescriptor, transaction);
                    //var time = int.Parse(propertyDescriptor.GetValue(transaction).ToString());
                    //return time.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
                }
                else if (propertyDescriptor.Name == "FloorCode")
                {
                    var floorCode = int.Parse(propertyDescriptor.GetValue(transaction).ToString());
                    return floorCode.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
                }
                else if (propertyDescriptor.Name == "LastVol")
                {
                    long lastVol = Convert.ToInt64(transaction.LastVol);
                    return lastVol.ToString() + "|" ?? DBNull.Value.ToString() + "|";
                }
                else
                {
                    return propertyDescriptor.GetValue(transaction).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                }
            }
            else
                throw new Exception("PropertyTypeRejected, do not know which string to return");
        }



        /// <summary>
        /// Build the message Transaction struct included values and properties of new Transaction and old Transaction
        /// </summary>
        /// <param name="objNew"></param>
        /// <param name="objOld"></param>
        /// <returns>Return a message string have struct along Transaction parameter</returns>
        public virtual string BuildMessageTransaction(PriceLibrary.ChartService.Transaction objNew,
            PriceLibrary.ChartService.Transaction objOld)
        {
            string oldMsg = BuildMessageTransactionOld(objNew, objOld);
            // _recorder.WriteBuildMessageTransaction(objNew, objOld, oldMsg);

            string newMsg = BuildMessageTransactionRefactor(objNew, objOld);
            if (!oldMsg.Equals(newMsg, StringComparison.Ordinal))
                throw new InvalidProgramException("New BuildMessageTransaction method is incorrect");
            else
                return newMsg;
        }

        private string BuildMessageTransactionOld(PriceLibrary.ChartService.Transaction objNew, PriceLibrary.ChartService.Transaction objOld)
        {
            string strMsgObj = MessageType.Transaction.ToString().ToUpper() + "#";
            string strNew = String.Empty;
            string strOld = String.Empty;
            string FloorCode = "10";

            if (objNew.MatchType == null)
            {
                objNew.MatchType = "U";
                objOld.MatchType = "U";
            }

            try
            {
                FloorCode = GetFloorCode(objNew, _datasource);
            }
            catch (KeyNotFoundException ex)
            {
                _log.Error("BuildMessageTransaction error. Not found Symbol  " + objNew.Symbol + " in Datasource BasePrices", ex);
            }

            string actual = FloorCode;

            if (IsMarketInfoSymbol(objNew.Symbol))
                FloorCode = GetFloorCode(objNew.Symbol);
            else ///Get FloorCode From datasource.BasePrices base on Transaction.Symbol
            {
                try
                {
                    var stockstatic = (PriceLibrary.PriceService.StockInfoStatic)_datasource.BasePrices[objNew.Symbol];
                    FloorCode = stockstatic.FloorCode.ToString();
                }
                catch (Exception ex)
                {
                    _log.Error("BuildMessageTransaction error. Not found Symbol  " + objNew.Symbol + " in Datasource BasePrices", ex);
                }
            }



            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.ChartService.Transaction));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        if (prop.Name == "Time")
                        {
                            var timeNew = int.Parse(prop.GetValue(objNew).ToString());
                            var timeOld = int.Parse(prop.GetValue(objOld).ToString());
                            strNew += timeNew.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
                            strOld += timeOld.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        else if (prop.Name == "FloorCode")
                        {
                            var timeNew = int.Parse(prop.GetValue(objNew).ToString());
                            var timeOld = int.Parse(prop.GetValue(objOld).ToString());
                            strNew += timeNew.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
                            strOld += timeOld.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        else if (prop.Name == "LastVol")
                        {
                            long lastVolNew = Convert.ToInt64(objNew.LastVol);
                            long lastVolOld = Convert.ToInt64(objOld.LastVol);
                            strNew += lastVolNew.ToString() + "|" ?? DBNull.Value.ToString() + "|";
                            strOld += lastVolOld.ToString() + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        else
                        {
                            strNew += prop.GetValue(objNew).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                            strOld += prop.GetValue(objOld).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                        }
                    }
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strNew + FloorCode + "#" + strOld + FloorCode;
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageTransaction", ex);
            }

            return strMsgObj;
        }

        /// <summary>
        /// Build the message Buy Put Through struct included values and properties of new Stock Buy and old Stock Buy
        /// </summary>
        /// <param name="objStatic"></param>
        /// <param name="objNew"></param>
        /// <param name="objOld"></param>
        /// <returns>Return a message string have struct along Stick Buy parameter</returns>
        public virtual string BuildMessageBuyPutThrough(PriceLibrary.PriceService.StockInfoStatic objStatic, 
            PriceLibrary.PriceService.StockInfoDyns objNew, 
            PriceLibrary.PriceService.StockInfoDyns objOld)
        {
            string strOld = BuildMessageBuyPutThroughOld(objStatic, objNew, objOld);
            string strNew = BuildMessagePutThroughRefactor(objStatic, objNew, objOld, TransactionType.Buy);
            if (strOld.Equals(strNew, StringComparison.Ordinal))
                return strNew;
            else
                throw new Exception("Buy Put through message unmatched");
        }

        private string BuildMessageBuyPutThroughOld(PriceLibrary.PriceService.StockInfoStatic objStatic, PriceLibrary.PriceService.StockInfoDyns objNew, PriceLibrary.PriceService.StockInfoDyns objOld)
        {
            string strMsgObj = "PUT#";
            string strDynsMsgNew = String.Empty;
            string strDynsMsgOld = String.Empty;
            try
            {
                //New----------------------------------------------------------
                strDynsMsgNew += objStatic.FloorCode.ToString("00") + "|";
                strDynsMsgNew += objNew.StockSymbol + "|";
                strDynsMsgNew += objNew.Best1Bid + "|";
                strDynsMsgNew += objNew.Best1BidVolume + "|";
                strDynsMsgNew += "B|";
                strDynsMsgNew += objNew.Status + "|";
                strDynsMsgNew += objNew.Time + "|";
                //Old----------------------------------------------------------
                strDynsMsgOld += objStatic.FloorCode.ToString("00") + "|";
                strDynsMsgOld += objOld.StockSymbol + "|";
                strDynsMsgOld += objOld.Best1Bid + "|";
                strDynsMsgOld += objOld.Best1BidVolume + "|";
                strDynsMsgOld += "B|";
                strDynsMsgOld += objOld.Status + "|";
                strDynsMsgOld += objOld.Time + "|";
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strDynsMsgNew + "#" + strDynsMsgOld;
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageBuyPutThrough", ex);
            }
            return strMsgObj;
        }

        public string BuildMessagePutThrough(PriceLibrary.PriceService.StockInfoStatic objStatic, PriceLibrary.PriceService.StockInfoDyns objNew, PriceLibrary.PriceService.StockInfoDyns objOld, TransactionType transType)
        {
            return BuildMessagePutThroughRefactor(objStatic, objNew, objOld, transType);
        }

        protected string BuildMessagePutThrough(PriceLibrary.PriceService.StockInfoStatic objStatic, PriceLibrary.PriceService.StockInfoDyns obj, TransactionType transType)
        {
            string strMsg = String.Empty;
            strMsg += objStatic.FloorCode.ToString("00") + "|";
            strMsg += obj.StockSymbol + "|"; 

            if (transType == TransactionType.Sell)
            {
                strMsg += obj.Best1Offer + "|";
                strMsg += obj.Best1OfferVolume + "|";
                strMsg += "S|";
            }
            else
                if (transType == TransactionType.Buy)
                {
                    strMsg += obj.Best1Bid + "|";
                    strMsg += obj.Best1BidVolume + "|";
                    strMsg += "B|";
                }
                else
                    throw new InvalidEnumArgumentException("Unknown TransactionType: " + transType.ToString());

            strMsg += obj.Status + "|";
            strMsg += obj.Time + "|";
            
            return strMsg;
        }

        /// <summary>
        /// Build the message Sell Put Through struct included values and properties of new Stock Sell and old Stock Sell
        /// </summary>
        /// <param name="objStatic"></param>
        /// <param name="objNew"></param>
        /// <param name="objOld"></param>
        /// <returns>Return a message string have struct along Stock Sell parameter</returns>
        public virtual string BuildMessageSellPutThrough(PriceLibrary.PriceService.StockInfoStatic objStatic,
            PriceLibrary.PriceService.StockInfoDyns objNew,
            PriceLibrary.PriceService.StockInfoDyns objOld)
        {
            string strOld = BuildMessageSellPutThroughOld(objStatic, objNew, objOld);
            string strNew = BuildMessagePutThroughRefactor(objStatic, objNew, objOld, TransactionType.Sell);
            if (strOld.Equals(strNew, StringComparison.Ordinal))
                return strNew;
            else
                throw new Exception("Build Sell Put Through unmatched");
        }


        private  string BuildMessagePutThroughRefactor(PriceLibrary.PriceService.StockInfoStatic objStatic, PriceLibrary.PriceService.StockInfoDyns objNew, PriceLibrary.PriceService.StockInfoDyns objOld, TransactionType transType)
        {
            string strMsgObj = "PUT#";
            string strDynsMsgNew = String.Empty;
            string strDynsMsgOld = String.Empty;
            try
            {
                //New----------------------------------------------------------
                strDynsMsgNew += BuildMessagePutThrough(objStatic, objNew, transType);
               
                //Old----------------------------------------------------------
                strDynsMsgOld += BuildMessagePutThrough(objStatic, objOld, transType);

                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strDynsMsgNew + "#" + strDynsMsgOld;
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageSellPutThrough", ex);
            }

            return strMsgObj;
        }

        private string BuildMessageSellPutThroughOld(PriceLibrary.PriceService.StockInfoStatic objStatic, PriceLibrary.PriceService.StockInfoDyns objNew, PriceLibrary.PriceService.StockInfoDyns objOld)
        {
            string strMsgObj = "PUT#";
            string strDynsMsgNew = String.Empty;
            string strDynsMsgOld = String.Empty;
            try
            {
                //New----------------------------------------------------------
                strDynsMsgNew += objStatic.FloorCode.ToString("00") + "|";
                strDynsMsgNew += objNew.StockSymbol + "|";
                strDynsMsgNew += objNew.Best1Offer + "|";
                strDynsMsgNew += objNew.Best1OfferVolume + "|";
                strDynsMsgNew += "S|";
                strDynsMsgNew += objNew.Status + "|";
                strDynsMsgNew += objNew.Time + "|";
                //Old----------------------------------------------------------
                strDynsMsgOld += objStatic.FloorCode.ToString("00") + "|";
                strDynsMsgOld += objOld.StockSymbol + "|";
                strDynsMsgOld += objOld.Best1Offer + "|";
                strDynsMsgOld += objOld.Best1OfferVolume + "|";
                strDynsMsgOld += "S|";
                strDynsMsgOld += objOld.Status + "|";
                strDynsMsgOld += objOld.Time + "|";
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strDynsMsgNew + "#" + strDynsMsgOld;
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageSellPutThrough", ex);
            }
            return strMsgObj;
        }

        public virtual string BuildMessageTransactionPutThrough(PriceLibrary.PriceService.Transaction objNew,
            PriceLibrary.PriceService.Transaction objOld)
        {
            string strMsgObj = "PUTEXEC#";
            string strNew = String.Empty;
            string strOld = String.Empty;
            int FloorCode = 10;
            ///Get FloorCode From datasource.BasePrices base on Transaction.Symbol
            try
            {
                var stockstatic = (PriceLibrary.PriceService.StockInfoStatic)_datasource.BasePrices[objNew.Symbol];
                FloorCode = stockstatic.FloorCode;
            }
            catch (Exception ex)
            {
                _log.Error("BuildMessageTransactionPutThrough. Error can not find Symbol in Datasource BasePrices", ex);
            }
            //Build Msg 
            try
            {
                //New----------------------------------------------------------
                strNew += FloorCode.ToString("00") + "|";
                strNew += objNew.Symbol + "|";
                strNew += objNew.Last + "|";
                strNew += objNew.LastVol + "|";
                //strNew += objNew.Time + "|";
                strNew += Convert.ToInt32(objOld.Time).ToString("000000") + "|";
                //Old----------------------------------------------------------                
                strOld += FloorCode + "|";
                strOld += objOld.Symbol + "|";
                strOld += objOld.Last + "|";
                strOld += objOld.LastVol + "|";
                //strOld += objNew.Time + "|";
                strOld += Convert.ToInt32(objOld.Time).ToString("000000") + "|";
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strNew + "#" + strOld ;
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMessageTransactionPutThrough", ex);
            }
            return strMsgObj;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Get type of properties by objeect
        /// </summary>
        /// <param name="e">MessageType</param>
        /// <returns>Return value of PropertyDescriptorCollection along object </returns>
        private PropertyDescriptorCollection GetPropertiesByOjbect(MessageType e)
        {
            PropertyDescriptorCollection properties = null;
            switch (e)
            {
                case MessageType.MarketInfo:
                    properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.PriceService.MarketInfo));
                    break;
                case MessageType.Stock:
                    properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.PriceService.StockInfoDyns));
                    break;
                case MessageType.Transaction:
                    properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.ChartService.Transaction));
                    break;
            }
            return properties;
        }

        /// <summary>
        /// Build the message StockInfoStatic struct included values and properties of StockInfoStatic
        /// </summary>
        /// <param name="objStatic"></param>
        /// <returns>Return a message string have struct along StockInfoStatic parameter</returns>
        private string BuildMsgStockInfoStatic(PriceLibrary.PriceService.StockInfoStatic objStatic)
        {
            string strMessage = String.Empty;
            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.PriceService.StockInfoStatic));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (!ListPropertyNameRejected.Contains(prop.Name) && prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        if (prop.Name == "FloorCode")
                        {
                            var FloorCode = int.Parse(prop.GetValue(objStatic).ToString());
                            strMessage += FloorCode.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        else
                        {
                            strMessage += GetPropertyStr(prop, objStatic);
                            //strMessage += prop.GetValue(objStatic).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        //strMessage += prop.GetValue(objStatic).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                        //strMessage += prop.Name +"="+prop.GetValue(objStatic).ToString().Trim() + "|\n" ?? DBNull.Value.ToString() + "|\n";
                        //strMessage += GetPropertyStr(prop, objStatic);
                    }
                }
            }
            catch (Exception ex)
            {
                _log.Error("Error BuildMsgStockInfoStatic", ex);
            }
            return strMessage;
        }

        
        #endregion
    }
}
